﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace Vbyte.SharpOrm.Config
{
    /// <summary>
    /// 绑定表属性
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = false)]
    public class BindTableAttribute : Attribute
    {
        /// <summary>
        /// 初始化一个<see cref="BindTableAttribute"/>实例
        /// </summary>
        /// <param name="connKey">绑定连接字符串键名</param>
        /// <param name="tableName">映射表名</param>
        public BindTableAttribute(string connKey, string tableName)
        {
            BindConnectionKey = connKey;
            Tables = new object[] { tableName };
        }

        /// <summary>
        /// 初始化一个<see cref="BindTableAttribute"/>实例
        /// </summary>
        public BindTableAttribute() { }
        
        /// <summary>
        /// 绑定的数据库连接字符串键名
        /// </summary>
        /// <value>连接字符串键名</value>
        public string BindConnectionKey { get; set; }

        private string primaryTabName = "";
        /// <summary>
        /// 获取主要绑定表名
        /// </summary>
        /// <returns></returns>
        public string PrimaryTableName
        {
            get { return primaryTabName; }
        }

        #region 关联表辅助
        private object[] tabsDef = null;

        //表别名词典库(别名, 表名)
        private Dictionary<string, string> TabAliastDict = new Dictionary<string, string>(1,
            StringComparer.InvariantCultureIgnoreCase);

        /// <summary>
        /// 绑定的数据库表名集合,单个表名或多个表关联绑定。
        /// 形如：new object[] { "A", "B", SQLTableLink.InnerJoin, "0|A.PID = 1|B.PID" }
        /// 表示内联AB表，条件是A.PID = B.PID
        /// <para>连接条件支持当前绑定实例的属性值，形如：A.PID = B.PID AND A.ProductName = '{ProductName}'，
        /// 表示绑定当前的产品名称。</para>
        /// </summary>
        public object[] Tables 
        {
            get { return tabsDef; }
            set 
            {
                if (value.Length == 1)
                {
                    primaryTabName = value[0].ToString();
                    TabAliastDict.Add("t0", primaryTabName);
                }
                else
                {
                    if (value.Length % 4 != 0)
                    {
                        throw new InvalidOperationException(@"表联接定义不正确，任意两个表连接必需依次指定4个条件：表TA[as a](字符型)、表TB[as b](字符型)、联接类型(枚举型)、联接条件(字符型)。");
                    }
                    else
                    {
                        string tabName = "",tabAlias = "";
                        string[] tnaDef = new string[0] { };
                        int idx = -1;
                        for (int i = 0; i < value.Length; i = i + 4)
                        {
                            if (value[i] != null && value[i].GetType() == typeof(string)
                                && value[i + 1] != null && value[i + 1].GetType() == typeof(string)
                                && value[i + 2] != null && value[i + 2].GetType() == typeof(SQLTableLink)
                                && value[i + 3] != null && value[i + 3].GetType() == typeof(string))
                            {
                                #region 配置表和别名词典
                                tabName = value[i].ToString().Trim();
                                idx = tabName.IndexOf(" as ", StringComparison.InvariantCultureIgnoreCase);
                                if (idx != -1)
                                {
                                    tabAlias = tabName.Substring(idx + 4).Trim();
                                    tabName = tabName.Substring(0, idx).Trim();
                                }
                                else
                                {
                                    if (tabName.IndexOf(" ") != -1)
                                    {
                                        tnaDef = tabName.Split(' ');
                                        tabName = tnaDef[0];
                                        tabAlias = tnaDef[1];
                                    }
                                    else
                                    {
                                        tabAlias = "t" + ((i > 0) ? i - 2 : i).ToString();
                                    }
                                }
                                //TA
                                TabAliastDict.Add(tabAlias.ToLower(), tabName);
                                //设置主要映射表名
                                if (i == 0) { primaryTabName = tabName;  }

                                //---------------------------------------------------------

                                tabName = value[i + 1].ToString().Trim();
                                idx = tabName.IndexOf(" as ", StringComparison.InvariantCultureIgnoreCase);
                                if (idx != -1)
                                {
                                    tabAlias = tabName.Substring(idx + 4).Trim();
                                    tabName = tabName.Substring(0, idx).Trim();
                                }
                                else
                                {
                                    if (tabName.IndexOf(" ") != -1)
                                    {
                                        tnaDef = tabName.Split(' ');
                                        tabName = tnaDef[0];
                                        tabAlias = tnaDef[1];
                                    }
                                    else
                                    {
                                        tabAlias = "t" + ((i > 0) ? i - 1 : i + 1).ToString();
                                    }
                                }
                                //TB
                                TabAliastDict.Add(tabAlias.ToLower(), tabName); 
                                #endregion
                            }
                            else
                            {
                                throw new InvalidOperationException("表联接定义的数据类型不正确！");
                            }
                        }
                    }
                }
                tabsDef = value; 
            }
        }

        /// <summary>
        /// 根据别名获取表名
        /// </summary>
        /// <param name="alias">表名</param>
        /// <returns>找到对应别名则返回，否则为空字符。</returns>
        public string GetTableNameByAlias(string alias)
        {
            string tabName = "";
            string key = alias.ToLower();
            if (TabAliastDict.ContainsKey(key))
            {
                tabName = TabAliastDict[key];
            }
            return tabName;
        }

        /// <summary>
        /// 根据绑定的表集合中的索引获取相关表名
        /// </summary>
        /// <param name="idx">表集合中的索引</param>
        /// <returns>找到对应别名则返回，否则为空字符。</returns>
        public string GetTableNameByIndex(int idx)
        {
            string tabName = "";
            if (idx > -1 && idx < TabAliastDict.Count)
            {
                int i=-1;
                foreach(string key in TabAliastDict.Keys)
                {
                    i++;
                    if (i== idx)
                    {
                        tabName = TabAliastDict[key];
                        break;
                    }
                }
            }
            return tabName;
        }

        /// <summary>
        /// 根据表名获取表映射的别名
        /// </summary>
        /// <param name="tabName">表名称</param>
        /// <returns>找到对应别名则返回，否则为空字符。</returns>
        public string GetTableAliasName(string tabName)
        {
            string aliasName = "";
            foreach (string key in TabAliastDict.Keys)
            {
                if (String.Compare(TabAliastDict[key], tabName, true) == 0)
                {
                    aliasName = key;
                    break;
                }
            }
            return aliasName;
        }

        /// <summary>
        /// 判断映射表中是否包含指定的表名
        /// </summary>
        /// <param name="tabName">数据表名全称</param>
        /// <returns></returns>
        public bool ContainsTable(string tabName)
        {
            return GetTableAliasName(tabName) != string.Empty;
        }

        /// <summary>
        /// 判断映射表中是否包含指定的表名
        /// </summary>
        /// <param name="tabName">数据表名全称</param>
        /// <param name="aliasName">映射别名</param>
        /// <returns></returns>
        public bool ContainsTable(string tabName, out string aliasName)
        {
            aliasName = GetTableAliasName(tabName);
            return aliasName != string.Empty;
        }

        /// <summary>
        /// 判定是否是单表绑定
        /// </summary>
        /// <returns></returns>
        public bool IsSingleTableBind()
        {
            return tabsDef.Length == 1;
        }

        /// <summary>
        /// 根据关联关系生成的联接表集合,形如：“User U inner join Group G on U.GroupID=G.GroupID [AND U.GroupID &gt; 0]”。
        /// </summary>
        /// <returns></returns>
        public string ToSqlCollectionTable()
        {
            return ToSqlCollectionTable(null);
        }

        /// <summary>
        /// 根据关联关系生成的联接表集合,形如：“User U inner join Group G on U.GroupID=G.GroupID [AND U.GroupID &gt; 0]”。
        /// </summary>
        /// <param name="filterInstance">当前约束实例</param>
        /// <returns></returns>
        public string ToSqlCollectionTable(object filterInstance)
        {
            if (tabsDef.Length == 1) return tabsDef[0].ToString();

            StringBuilder sql = new StringBuilder();
            string joinFormat = "{0} {2} {1} ON {3}";
            string onConstrains = string.Empty;
            //绑定实例属性值匹配模式 {PropName}
            string instancePropPattern = "\\{(\\w+)\\}";
            for (int i = 0; i < tabsDef.Length; i = i + 4)
            {
                onConstrains = ForceUseTableAlias(tabsDef[i + 3].ToString());
                if (filterInstance != null && Regex.IsMatch(onConstrains, instancePropPattern, RegexOptions.IgnoreCase))
                {
                    onConstrains = Regex.Replace(onConstrains, instancePropPattern,
                        delegate(Match m)
                        {
                            return filterInstance.GetType().GetProperty(m.Groups[1].Value)
                                .GetGetMethod()
                                .Invoke(filterInstance, null).ToString();
                        });
                }

                sql.Append(string.Format(joinFormat,
                    ForceTableWithAlias(tabsDef[i].ToString()), //TA
                    ForceTableWithAlias(tabsDef[i + 1].ToString()), //TB
                    GetLinkSql((SQLTableLink)tabsDef[i + 2]), //JOIN
                    onConstrains)); //ON
            }
            return sql.ToString();
        }

        private string ForceTableWithAlias(string tabDef)
        {
            tabDef = tabDef.Trim();
            if (tabDef.IndexOf(" ") == -1)
            {
                return tabDef + " AS " + GetTableAliasName(tabDef);
            }
            else
            {
                return tabDef;
            }
        }

        //强制使用表的别名前缀
        private string ForceUseTableAlias(string linkCond)
        {
            string aliasName = "";
            //([\w_\-\[\]\d]+)\.([\w_\-\[\]\d]+)
            return Regex.Replace(linkCond, "([\\w_\\-\\[\\]\\d]+)\\.([\\w_\\-\\[\\]\\d]+)", new MatchEvaluator(delegate(Match m)
            {
                //System.Diagnostics.Debug.WriteLine(m.Value);
                if (ContainsTable(m.Groups[1].Value.Trim('[', ']'), out aliasName))
                {
                    return aliasName + "." + m.Groups[2].Value;
                }
                else
                {
                    return m.Value;
                }
            }),
            RegexOptions.IgnoreCase);
        }

        private string GetLinkSql(SQLTableLink link)
        {
            string[] joinArray = new string[] { "Inner Join",
                "Left Join",
                "Right Join",
                "Full Join",
                "CROSS JOIN" };
            return joinArray[(byte)link];
        }

        #endregion

        /// <summary>
        /// 表联接的其他条件
        /// </summary>
        public string LinkConditions { get; set; }

        /// <summary>
        /// 主键名称，多个主键使用英文逗号分隔。
        /// </summary>
        public string PrimaryKeys { get; set; }

    }

}